package labb3;

import labb3.FileHandling.CollectionLoader;
import labb3.FileHandling.CollectionSaver;

import java.io.IOException;
import java.util.ArrayList;
import static java.util.Collections.sort;
import java.util.regex.PatternSyntaxException;

/**
 * MediaCollection will give the possibility to 'collect' Media.
 *
 * @author Mikko Puustinen
 * @version 0.5, 2007-okt-18
 */
class MediaCollection {
    private ArrayList<Media> media;
    private final ArrayList<ChangeListener> listeners;
    private boolean isChanged = false;

    /**
     * Creates a new (empty) MediaCollection.
     */
    public MediaCollection() {
        media = new ArrayList<Media>();
        listeners = new ArrayList<ChangeListener>();
    }

    /**
     * Adds a Media object to the MediaCollection.
     *
     * @param media Media to add.
     */
    public void addMedia(Media media) {
        this.media.add(media);
        listChange();
        isChanged = true;
    }

    /**
     * Removes the specified Media from the MediaCollection.
     *
     * @param index position of the Media to remove.
     *
     * @throws IndexOutOfBoundsException if the specified index does not exist.
     */
    public void removeMedia(int index) throws IndexOutOfBoundsException {
        this.media.remove(index);
        listChange();
        isChanged = true;
    }

    /**
     * Performs an edit of the Media object with the specified data.
     *
     * @param index    index of the Media object to change.
     * @param prodDate new production date.
     * @param relDate  new release date.
     * @param purDate  new purchase date.
     * @param rating   new rating.
     */
    public void editMedia(int index, String prodDate, String relDate, String purDate, String rating) {
        Media m = media.get(index);
        m.setProductionDate(prodDate);
        m.setReleaseDate(relDate);
        m.setPurchaseDate(purDate);
        m.setRating(rating);
        listUpdate();
        isChanged = true;
    }

    /**
     * Returns the Media object at the specified position in the
     * MediaCollection.
     *
     * @param index index of the Media to return.
     *
     * @return Media at the specified position.
     *
     * @throws IndexOutOfBoundsException if the index does not exist.
     */
    public Media getMediaAt(int index) throws IndexOutOfBoundsException {
        return media.get(index);
    }

    /**
     * Returns a clone of the MediaCollection.
     *
     * @return clone of the ArrayList containing Media object.
     */
    public ArrayList<Media> getCollectionClone() {
        ArrayList<Media> clone = new ArrayList<Media>();
        clone.addAll(media);

        return clone;
    }

    /**
     * Changes the list which the MediaCollection uses to build the database.
     *
     * @param media reference to the ArrayList containing Media to use as
     *              MediaCollection.
     */
    public void setMediaCollection(ArrayList<Media> media) {
        this.media = media;
    }

    /**
     * Is the MediaCollection changed (i.e media added, deleted, edited and it
     * haven't been saved)
     *
     * @return true if there has been a change, false otherwise.
     */
    public boolean isChanged() {
        return isChanged;
    }

    /**
     * Notify all listeners that there has been a change to the
     * MediaCollection.
     */
    private void listChange() {
        listChange(media);
    }

    /**
     * Notify all listeners that there has been a change to the MediaCollection.
     * Sends the specified ArrayList to all listeners.
     *
     * @param list reference to the new list that the listener can use to create
     *             its own list.
     */
    private void listChange(ArrayList<Media> list) {
        for (ChangeListener c : listeners)
            c.notifyListChange(list);
    }

    /**
     * Notify all the listeners that there has been some updates made to the
     * MediaCollection (i.e some media were changed or the list has been loaded
     * from other location)
     */
    private void listUpdate() {
        for (ChangeListener c : listeners)
            c.notifyListUpdate();
    }

    /**
     * Sort the collection.
     */
    public void sortCollection() {
        sort(media);
        listChange();
    }

    /**
     * Finds the Media with the matching title and returns the position in the
     * MediaCollection.
     *
     * @param title the title to search for.
     *
     * @return position of the Media with the matching title, -1 if it does not
     *         exist.
     */
    public int getTitleIndex(String title) {
        int i = 0;
        for (Media m : media) {
            if (m.getTitle().matches("(?i)" + title))
                return i;
            i++;
        }
        return -1;
    }

    /**
     * Searches the MediaCollection for Media with a title matching the search
     * string (ignores case).
     *
     * @param title String to search for in the title.
     *
     * @return a sorted ArrayList with all the Media matching the search
     *         string.
     *
     * @see java.util.Collections#sort
     */
    public ArrayList<Media> searchByTitle(String title) {
        ArrayList<Media> ar = new ArrayList<Media>();
        for (Media m : media) {
            try {
                if (m.getTitle().matches("(?i).*" + title + ".*")) {
                    ar.add(m);
                }
            } catch (PatternSyntaxException e) {
                //Don't have to do anything
            }
        }

        return ar;
    }

    /**
     * Let an external class notify all listeners about the ArrayList with the
     * search results.
     *
     * @param list ArrayList with the search results.
     */
    public void notifySearchResult(ArrayList<Media> list) {
        listChange(list);
    }

    /**
     * Saves a MediaCollection using the CollectionSaver.
     *
     * @param saver Save method.
     *
     * @throws java.io.IOException if the MediaCollection could not be saved.
     * @see labb3.FileHandling.CollectionSaver
     */
    public void saveCollection(CollectionSaver saver) throws IOException {
        saver.saveCollection(media);
        isChanged = false;
    }

    /**
     * Loads a MediaCollection using the CollectionLoader.
     *
     * @param loader Load method.
     *
     * @throws java.io.IOException    if the file could not be read or some
     *                                other error occured while reading the
     *                                file.
     * @throws ClassNotFoundException not used.
     * @see labb3.FileHandling.CollectionLoader
     */
    public void loadCollection(CollectionLoader loader) throws IOException, ClassNotFoundException {
        setMediaCollection(loader.loadCollection());
        listChange();
        isChanged = false;
    }

    /**
     * Adds a class to list of listeners who will be notified upon a change
     * occures in the MediaCollection.
     *
     * @param listener the class to add to the list.
     */
    public void addListener(ChangeListener listener) {
        listeners.add(listener);
    }
}
